package org.onion.pattern.event.outbox.converter;

import cn.hutool.core.annotation.AnnotationUtil;
import com.github.f4b6a3.uuid.UuidCreator;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.onion.pattern.event.domain.DomainEvent;
import org.onion.pattern.event.domain.Event;
import org.onion.pattern.event.io.codec.JacksonEventCodec;
import org.onion.pattern.event.io.codec.ProtobufEventCodec;
import org.onion.pattern.event.outbox.OutboxEventConverter;
import org.onion.pattern.event.outbox.annotation.Outbox;
import org.onion.pattern.event.outbox.data.OutboxEventInfo;
import org.onion.pattern.event.outbox.data.OutboxEventStatus;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Objects;

@Slf4j
public class DefaultOutboxEventConverter implements OutboxEventConverter {

    public static final DefaultOutboxEventConverter INSTANCE = new DefaultOutboxEventConverter();

    @Override
    public boolean support(Object event) {
        return true;
    }

    @Override
    public boolean support(OutboxEventInfo event) {
        return true;
    }

    @Override
    public OutboxEventInfo convert(Object msg) {
        OutboxEventInfo eventInfo = new OutboxEventInfo();
        eventInfo.setOutBoxEventStatus(OutboxEventStatus.CREATED);
        eventInfo.setVer(1L);
        eventInfo.setEventKey(UuidCreator.getTimeOrderedEpoch().toString());
        eventInfo.setEventTime(System.currentTimeMillis());
        eventInfo.setSerializer(JacksonEventCodec.TYPE);
        eventInfo.setTopic("_none_");
        eventInfo.setSubTopic(StringUtils.replace(msg.getClass().getName(), ".", "-"));
        eventInfo.setProps(null);
        eventInfo.setChannel("local");
        eventInfo.setDomainType(msg.getClass().getName());
        eventInfo.setDomainId("_none_");
        eventInfo.setGroup(OutboxEventInfo.NONE_GROUP);

        if (msg instanceof Event) {
            Event event = (Event) Objects.requireNonNull(msg);
            eventInfo.setEventKey(event.eventKey());
            if(event.eventTime()!=null){
                LocalDateTime localDateTime = event.eventTime();
                Instant eventTimeInstant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
                eventInfo.setEventTime(ObjectUtils.defaultIfNull(eventTimeInstant.toEpochMilli(), eventInfo.getEventTime()));
            }
        }

        if (msg instanceof DomainEvent) {
            DomainEvent domainEvent = (DomainEvent) msg;
            eventInfo.setDomainId(domainEvent.domainId().toString());
        }

        Outbox outbox = AnnotationUtil.getAnnotation(msg.getClass(), Outbox.class);
        if (outbox != null) {
            eventInfo.setGroup(StringUtils.defaultIfBlank(outbox.value(), eventInfo.getGroup()));
            eventInfo.setSerializer(StringUtils.defaultIfBlank(outbox.serializer(), eventInfo.getSerializer()));
        }

        if (JacksonEventCodec.INSTANCE.type().equalsIgnoreCase(eventInfo.getSerializer())) {
            eventInfo.setPayload(JacksonEventCodec.INSTANCE.serialize(msg));
        } else if (ProtobufEventCodec.INSTANCE.type().equalsIgnoreCase(eventInfo.getSerializer())) {
            eventInfo.setPayload(ProtobufEventCodec.INSTANCE.serialize(msg));
        } else {
            eventInfo.setPayload(JacksonEventCodec.INSTANCE.serialize(msg));
            log.warn("使用未知的的序列化,将使用jackson 序列化 "+eventInfo.getSerializer());
        }

        return eventInfo;
    }

    @SneakyThrows
    @Override
    public Object convert(OutboxEventInfo eventInfo) {
        if (StringUtils.equalsIgnoreCase(ProtobufEventCodec.INSTANCE.type(), eventInfo.getSerializer())) {
            return ProtobufEventCodec.INSTANCE.deserialize(eventInfo.getPayload(), Class.forName(eventInfo.getDomainType()));
        }

        return JacksonEventCodec.INSTANCE.deserialize(eventInfo.getPayload(), Class.forName(eventInfo.getDomainType()));
    }

}
